Многомерные статические массивы
В си, наряду с одномерными, существуют и многомерные массивы. Например, двумерный массив: его можно представлять как массив массивов, или как матрицу. Размерность массива может быть и больше: трёхмерные, четырёхмерные и т.д.
Синтаксис остаётся прежним, добавляется только новая размерность
<тип> <имя>[размерность1][размерность2]...;
Например, двумерный массив
int a[2][3];
Трёхмерный массив
int a[3][4][5];
Доступ до элементов массива осуществляется также, как и в одномерном массиве
#define _CRT_SECURE_NO_WARNINGS
#include <conio.h>
#include <stdio.h>
#define SIZE 5
void main() {
int M[3][3];
unsigned i, j;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
M[i][j] = i * j;
}
}
do {
printf("enter indexes:\n");
scanf("%d", &i);
scanf("%d", &j);
if (i < 3 && j < 3) {
printf("M[%d][%d] == %d\n", i, j, M[i][j]);
} else {
break;
}
} while (1);
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
printf("\t%d", M[i][j]);
}
printf("\n");
}
_getch();
}
Особенностью является то, что по своему строению многомерный массив является обыкновенным, "одномерным", массивом. Все элементы расположены друг за другом. Доступ до элемента a[i][j]
– по существу сдвиг на i*число столбцов + j
. В двумерном массиве, таким образом, элементы расположены "по рядам", в трёхмерном - "по слоям", внутри которых элементы расположены "по рядам" и т.д.
В связи с этим, при начальной инициализации опускать размерность можно только в первых квадратных скобках:
int a[][3] = {0};
int b[][5][25] = {0};
Компилятор будет знать в таком случае сдвиг, необходимый для доступа к элементу.
С этим связаны и особенности начальной инициализации. Так как многомерный массив по сути одномерный, то его начальную инициализацию можно провести так
int a[2][3] = {1, 2, 3, 4, 5, 6};
Можно опустить первую размерность
int a[][2][3] = {1, 2, 3, 34, 5, 6, 7, 8, 9, 10, 11, 12};
Можно с помощью фигурных скобок сделать данные более удобными для чтения
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}
или
int a[][3][4] =
{{{1, 2, 3, 4}, {2, 4, 6, 8}, {3, 6, 9, 12}},
{{1, 2, 3, 4}, {2, 4, 6, 8}, {3, 6, 9, 12}},
{{1, 2, 3, 4}, {2, 4, 6, 8}, {3, 6, 9, 12}}};
Также, как и в одномерных массивах, если заявлено данных больше, чем указано при инициализации, то оставшиеся заполняются нулями. Например, единичная матрица 3 на 3
int zero3[3][3] = {{1}, {0, 1}, {0, 0, 1}};
Из того, что многомерный массив является одномерным по структуре, вытекают некоторые интересные свойства. Например, доступ до элемента может быть осуществлён через его порядковый номер
a[i][j] === a[0][i*число столбцов + j]
и т.д.
Примеры
1. Отсортируем двумерный массив методом пузырька. Для сортировки обычно используется два подхода - превращение двумерного массива в одномерный, сортировка, обратно превращение одномерного в двумерный, либо запутанное обращение к элементам через индекс. Можно сделать всё проще: работать с многомерным массивом как с одномерным
#include <conio.h>
#include <stdio.h>
#define ROWS 4
#define COLS 3
void main() {
int a[ROWS][COLS] =
{{1, 4, 5},
{2, 6, 8},
{1, 0, 9},
{4, 2, 8}};
int i, j, tmp, flag;
do {
flag = 0;
for (i = 1; i < ROWS*COLS; i++) {
if (a[0][i] < a[0][i-1]) {
tmp = a[0][i];
a[0][i] = a[0][i-1];
a[0][i-1] = tmp;
flag = 1;
}
}
} while(flag);
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++) {
printf("%3d", a[i][j]);
}
printf("\n");
}
_getch();
}
Замечание: по стандарту явно такое поведение явно не определено.
2. Даны координаты x и y точки, полученные в ходе фотосъёмки. Известно, сколько кадров в секунду делала камера. Вычислить скорость в каждый момент времени и среднюю скорость за всё время.
#include <conio.h>
#include <stdio.h>
#include <math.h>
#define SIZE 10
void main() {
float a[2][SIZE] =
{{1.03, 1.52, 2.11, 2.53, 3.08, 3.48, 3.98, 4.51, 5.02, 5.17},
{1.03, 2.45, 4.13, 5.64, 7.22, 8.73, 10.32, 11.75, 13.28, 14.87}};
float velocity[SIZE-1];
float speed = 0.0;
float vx, vy;
float dt = 0.1;
int i;
for (i = 0; i < SIZE-1; i++) {
vx = ( a[0][i+1] - a[0][i] ) / dt;
vy = ( a[1][i+1] - a[1][i] ) / dt;
velocity[i] = sqrt(vx*vx + vy*vy);
speed += velocity[i];
}
speed /= (float)(SIZE - 1);
for (i = 1; i < SIZE; i++) {
printf("v[%d] = %.3f m/s\n", i, velocity[i-1]);
}
printf("mean velocity = %.3f", speed);
_getch();
}
3. Массив используется как карта, где число 2 означает начало, а 3 - конец пути. Программа сначала находит координаты этих точек, после этого вычисляет расстояние Манхеттена (сколько нужно пройти по x и y от начала до конца) и расстояние по Евклиду (как гипотенузу прямоугольного треугольника).
#include <conio.h>
#include <stdio.h>
#include <math.h>
#define SIZE 5
#define START 2
#define FINISH 3
void main() {
char field[SIZE][SIZE] = {
{0, 0, 0, 0, 0},
{2, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 3}
};
unsigned i, j;
unsigned x, y;
char xFound = 0;
int X, Y;
char XFound = 0;
unsigned manhattanDist;
float euclidDist;
for (i = 0; i < SIZE; i++) {
for (j = 0; j < SIZE; j++) {
if (field[i][j] == START) {
x = i;
y = j;
xFound = 1;
if (XFound) {
break;
}
}
if (field[i][j] == FINISH) {
X = i;
Y = j;
XFound = 1;
if (xFound) {
break;
}
}
}
if (xFound && XFound) {
break;
}
}
if (!(xFound && XFound)) {
printf("Error: corrupted data\n");
getch();
exit(1);
}
printf("(x,y) = %d, %d\n(X,Y)= %d, %d\n", x, y, X, Y);
manhattanDist = abs((int)(x-X)) + abs((int)(y-Y));
//тоже самое, что и sqrt((x-X)*(x-X)+(y-Y)*(y-Y))
x -= X;
y -= Y;
euclidDist = sqrt((float)(x*x + y*y));
printf("Manhattan dist. = %d\nEuclid dist. = %.3f", manhattanDist, euclidDist);
_getch();
}
4. Пользователь вводит 10 слов. Вывести слово с максимальной длиной. Программа внешне совершенно простая, единственная проблема - считывание и вывод слова. Так как слова храняться в двумерном массиве, то указатель на words[i][0] - это начало нового слова. Также не забываем об ограничении на длину при вводе.
#include <conio.h>
#include <stdio.h>
#define SIZE 10
#define MAX_LENGTH 128
void main() {
//Массив хранит 10 слов максимум по 128 символов
char words[SIZE][MAX_LENGTH];
unsigned i, j, maxLength;
//Так как длина слова ограничена 127 символами, то типа char хватит
unsigned char counter[SIZE];
for (i = 0; i < SIZE; i++) {
//Считываем слова. words[i][0] - это символ, нам нужен
//адрес, начиная с которого можно писать в массив
fgets(&words[i][0], MAX_LENGTH - 1, stdin);
j = 0;
//Считаем длину слова
while (words[i][j]) {
j++;
}
counter[i] = j;
}
//Ищем слово с максимальной длиной
maxLength = counter[0];
j = 0;
for (i = 1; i < SIZE; i++) {
if (counter[i] > maxLength) {
maxLength = counter[i];
j = i;
}
}
//Выводим слово на печать. При выводе строки
//необходимо передавать указатель
printf("%s", &words[j][0]);
_getch();
}